package com.weizhu.swufer.core.filter;

import com.weizhu.swufer.constant.SwuferConstant;
import com.weizhu.swufer.core.annotation.SwuferFilter;
import com.weizhu.swufer.core.client.Invoker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 过滤器管理
 */
public class FilterManager {

    private static final Logger logger = LoggerFactory.getLogger(FilterManager.class);

    private List<Filter> providerFilters;

    private List<Filter> clientFilters;

    private static volatile FilterManager instance;

    private FilterManager(){
        this.providerFilters = new ArrayList<>();
        this.clientFilters = new ArrayList<>();
    }

    public static FilterManager getInstance(){
        if (instance == null){
            synchronized (FilterManager.class){
                if (instance == null){
                    instance = new FilterManager();
//                    instance.addClientFilter(new LogFilter());
//                    instance.addClientFilter(new ClientContextFilter());
//                    instance.addClientFilter(new LimitFilter());
                }
            }
        }
        return instance;
    }

    public void registerFilter(Object obj){
        Class<?>[] interfaces = obj.getClass().getInterfaces();
        if (Arrays.stream(interfaces).noneMatch(x -> x.isAssignableFrom(Filter.class))){
            logger.warn("{} is not assignable from com.weizhu.swufer.core.filter.Filter.class",
                    obj.getClass().getName());
            return;
        }
        SwuferFilter annotation = obj.getClass().getAnnotation(SwuferFilter.class);
        String[] type = annotation.type();
        if (type == null || type.length == 0){
            return;
        }
        List<String> typeList = Arrays.stream(type).collect(Collectors.toList());

        if (typeList.contains(SwuferConstant.FILTER_TYPE_PROVIDER)){
            providerFilters.add((Filter) obj);
            logger.info("register provider filter:" + obj.getClass().getName());
        }
        if (typeList.contains(SwuferConstant.FILTER_TYPE_CLIENT)){
            clientFilters.add((Filter) obj);
            logger.info("register client filter:" + obj.getClass().getName());
        }
    }

    public void addClientFilter(Filter filter){
        this.clientFilters.add(filter);
        logger.info("filter:" + filter.getClass().getName() + " has been register");
    }

    public void addProviderFilter(Filter filter){
        this.providerFilters.add(filter);
    }

    public FilterChain buildClientFilterChain(){
        FilterChain filterChain = new FilterChain();
        filterChain.addAll(clientFilters);
        return filterChain;
    }

    public FilterChain buildProviderFilterChain(){
        FilterChain filterChain = new FilterChain();
        filterChain.addAll(providerFilters);
        return filterChain;
    }

    public List<Filter> getProviderFilters() {
        return providerFilters;
    }

    public void setProviderFilters(List<Filter> providerFilters) {
        this.providerFilters = providerFilters;
    }

    public List<Filter> getClientFilters() {
        return clientFilters;
    }

    public void setClientFilters(List<Filter> clientFilters) {
        this.clientFilters = clientFilters;
    }

    public void sort() {
        Comparator<Filter> comparator = (o1, o2) -> {
            SwuferFilter a1 = o1.getClass().getAnnotation(SwuferFilter.class);
            SwuferFilter a2 = o2.getClass().getAnnotation(SwuferFilter.class);
            int order1 = a1.order();
            int order2 = a2.order();
            return order1 - order2;
        };
        clientFilters.sort(comparator);
        providerFilters.sort(comparator);
    }

    private int getOrder(Filter f) {
        if (LogFilter.class.equals(f.getClass())) {
            return 100;
        }else if (ClientContextFilter.class.equals(f.getClass())){
            return -999;
        }else if (LimitFilter.class.equals(f.getClass())){
            return 50;
        }
        return 0;
    }
}
